home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------------
-
- newsrc.c
-
- This module handles newsrc-format files, including opening
- and saving user group lists from and to disk in newsrc format,
- and getting and sending newsrc files from and to remote hosts.
-
- Copyright © 1994-1995, Northwestern University.
-
- ----------------------------------------------------------------------------*/
-
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <ctype.h>
-
- #include "glob.h"
- #include "newsrc.h"
- #include "dialog.h"
- #include "prefs.h"
- #include "ftp.h"
- #include "group.h"
- #include "full.h"
- #include "newswatcher.h"
- #include "mark.h"
- #include "news.h"
- #include "sfutil.h"
- #include "status.h"
- #include "wind.h"
- #include "text.h"
- #include "memutil.h"
- #include "fileutil.h"
- #include "strutil.h"
- #include "resutil.h"
- #include "ic.h"
-
-
-
- #define kHostDlg 129 /* Remote host dialog */
- #define kRemoteHost 4
- #define kRemoteUsername 6
- #define kRemotePassword 8
- #define kRemotePath 10
- #define kAutoGetPut 11
- #define kSavePassword 12
-
- #define kCheckSaveID 133 /* Save confirm dialog */
-
- #define kDeletedGroupsDlg 131 /* Deleted groups dialog */
- #define KDelScrollingTextItem 4 /* item number of scrolling text field */
-
-
-
- /* The following globals are used when parsing newsrc lists. */
-
- static TGroup **gUserGroupArray; /* handle to user group array under construction */
- static short gNumUserGroups; /* number of user groups */
- static short gNumUserGroupsAllocated; /* number of user groups allocated */
- static Handle gDeleted; /* handle to list of deleted groups */
- static long gDeletedLen; /* length of list of deleted groups */
- static long gDeletedAllocated; /* number of bytes allocated for gDeleted */
- static char *gUnsubscribed; /* pointer to next location to move unsubscribed groups */
- static char *gPos; /* current parsing position in newsrc list */
- static TGroup gTheGroup; /* current group under construction */
-
- /* The following global variables are used when constructing newsrc lists. */
-
- static Handle gNewsrc; /* newsrc list under construction */
- static long gNewsrcLength; /* length of newsrc */
- static long gNewsrcAllocated; /* number of bytes allocated in newsrc */
-
-
-
- /*----------------------------------------------------------------------------
- DoHostDialog
-
- Present the remote host dialog.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoHostDialog (void)
- {
- DialogPtr dlg = nil;
- short item;
- CStr255 tempStr;
- short len;
- CStr255 host;
- CStr255 username;
- char path[32];
- char password[32];
- OSErr err = noErr;
-
- err = MyGetNewDialog(kHostDlg, ok, cancel, &dlg);
- if (err != noErr) return err;
- RestoreMovableModalDialogPosition(dlg, gPrefs.hostLoc);
- strcpy(host, gPrefs.ftpNewsrcHost);
- DlgSetCString(dlg, kRemoteHost, host);
- SetItemHostAddress(dlg, kRemoteHost);
- SetItemMaxLength(dlg, kRemoteHost, 255);
- strcpy(username, gPrefs.ftpNewsrcUsername);
- DlgSetCString(dlg, kRemoteUsername, username);
- SetItemUSAsciiNoBlank(dlg, kRemoteUsername);
- SetItemMaxLength(dlg, kRemoteUsername, 255);
- strcpy(password, gPrefs.ftpNewsrcPassword);
- len = strlen(password);
- memset(tempStr, '•', len);
- tempStr[len] = 0;
- DlgSetCString(dlg, kRemotePassword, tempStr);
- SetItemPassword(dlg, kRemotePassword, password);
- SetItemMaxLength(dlg, kRemotePassword, 31);
- strcpy(path, gPrefs.ftpNewsrcPath);
- DlgSetCString(dlg, kRemotePath, path);
- SetItemMaxLength(dlg, kRemotePath, 31);
- DlgSetCheck(dlg, kAutoGetPut, gPrefs.autoFetchNewsrc);
- DlgSetCheck(dlg, kSavePassword, gPrefs.saveFtpNewsrcPassword);
- if (*host == 0) {
- SelectDialogItemText(dlg, kRemoteHost, 0, 0);
- } else if (*username == 0) {
- SelectDialogItemText(dlg, kRemoteUsername, 0, 0);
- } else if (*password == 0) {
- SelectDialogItemText(dlg, kRemotePassword, 0, 0);
- } else if (*path == 0) {
- SelectDialogItemText(dlg, kRemotePath, 0, 0);
- } else {
- SelectDialogItemText(dlg, kRemoteHost, 0, 0x7fff);
- }
-
- do {
- DlgEnableItem(dlg, ok, *host != 0 && *username != 0 && *password != 0 && *path != 0);
- MyMovableModalDialog(dlg, DialogFilter, &item);
- switch (item) {
- case kRemoteHost:
- DlgGetCString(dlg, item, host);
- break;
- case kRemoteUsername:
- DlgGetCString(dlg, item, username);
- break;
- case kRemotePath:
- DlgGetCString(dlg, item, path);
- break;
- case kAutoGetPut:
- case kSavePassword:
- DlgToggleCheck(dlg, item);
- break;
- }
- } while (item != ok && item != cancel);
-
- if (item == ok) {
- strcpy(gPrefs.ftpNewsrcHost, host);
- strcpy(gPrefs.ftpNewsrcUsername, username);
- strcpy(gPrefs.ftpNewsrcPassword, password);
- strcpy(gPrefs.ftpNewsrcPath, path);
- gPrefs.autoFetchNewsrc = DlgGetCheck(dlg, kAutoGetPut);
- gPrefs.saveFtpNewsrcPassword = DlgGetCheck(dlg, kSavePassword);
- }
- SaveMovableModalDialogPosition(dlg, &gPrefs.hostLoc);
- err = DoClose(dlg);
- if (err != noErr) return err;
- return item == ok ? noErr : userCanceledErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- Skip
-
- Skip white space in a newsrc line.
- ----------------------------------------------------------------------------*/
-
- static void Skip (void)
- {
- while (*gPos == ' ' || *gPos == '\t') gPos++;
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetUnreadList
-
- Parse the list of read article ranges in a newsrc line and
- convert it to a linked list of unread article ranges.
-
- Exit: function result = error code.
- syntaxError = true if syntax error.
- gTheGroup.unread = handle to unread list.
- gTheGroup.lastMess = highest article number read, or 0 if
- unread list is empty.
- ----------------------------------------------------------------------------*/
-
- static OSErr GetUnreadList (Boolean *syntaxError)
- {
- long firstUnread, firstRead, lastRead;
- OSErr err = noErr;
-
- gTheGroup.unread = nil;
- gTheGroup.numUnread = 0;
-
- firstUnread = 1;
- lastRead = 0;
- Skip();
-
- while (*gPos != CR) {
- Skip();
- if (!isdigit(*gPos)) goto exit1;
- firstRead = CrackNum(&gPos);
- if (firstRead == 0) firstRead = 1;
- if (firstRead < firstUnread) goto exit1;
- Skip();
- if (*gPos == '-') {
- gPos++;
- Skip();
- if (!isdigit(*gPos)) goto exit1;
- lastRead = CrackNum(&gPos);
- if (lastRead < firstRead) goto exit1;
- } else {
- lastRead = firstRead;
- }
- if (firstUnread < firstRead) {
- err = AppendUnreadRange(firstUnread, firstRead-1, &gTheGroup);
- if (err != noErr) goto exit2;
- }
- firstUnread = lastRead+1;
- Skip();
- if (*gPos == ',') {
- gPos++;
- Skip();
- }
- }
-
- gPos++;
- err = AppendUnreadRange(firstUnread, 0x7fffffff, &gTheGroup);
- if (err != noErr) goto exit2;
- gTheGroup.lastMess = lastRead;
- *syntaxError = false;
- return noErr;
-
- exit1:
-
- DisposeGroupUnreadList(&gTheGroup);
- *syntaxError = true;
- return noErr;
-
- exit2:
-
- DisposeGroupUnreadList(&gTheGroup);
- return err;
- }
-
-
- /*----------------------------------------------------------------------------
- RecordDeletedGroup
-
- Record a user error message for a group which was deleted while
- parsing a newsrc file.
-
- Entry: groupName = the group name.
- reason = the reason the group was deleted:
- 1: Syntax error in newsrc line.
- 2: Group not in full group list.
- 3: Group deleted on server.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr RecordDeletedGroup (char *groupName, short reason)
- {
- CStr255 reasonStr;
- char msg[512];
- short len, reasonStrIndex;
- OSErr err = noErr;
-
- switch (reason) {
- case 1:
- reasonStrIndex = kStrSyntaxErr;
- break;
- case 2:
- reasonStrIndex = kStrNotInFullGroupList;
- break;
- case 3:
- reasonStrIndex = kStrGroupDeletedOnServer;
- break;
- }
- GetCString(reasonStrIndex, reasonStr);
- sprintf(msg, "%s: %s\r", groupName, reasonStr);
- len = strlen(msg);
- if (gDeletedLen + len > gDeletedAllocated) {
- gDeletedAllocated += 1000;
- err = MySetHandleSize(gDeleted, gDeletedAllocated);
- if (err != noErr) return err;
- }
- BlockMoveData(msg, *gDeleted + gDeletedLen, len);
- gDeletedLen += len;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- ProcessOneNewsrcLine
-
- Parse and processe one line from a newsrc list.
-
- Entry: gPos = pointer to beginning of newsrc line.
-
- Exit: function result = error code.
- gPos = pointer to beginning of next newsrc line.
-
- There are four possible kinds of newsrc lines:
-
- 1. Lines with syntax errors. These lines are recorded in the gDeleted
- array to be reported to the user later.
-
- 2. Lines for deleted groups (group name not in full group list).
- These lines are recorded in the gDeleted array to be reported to
- the user later.
-
- 3. Subscribed lines. These lines are parsed and appended to the end of the
- gUserGroupArray, with the following TGroup fields initialized:
-
- nameOffset = offset in gGroupNames of group name.
- firstMess = 1.
- lastMess = highest article number read on newsrc line, or 0 if read article
- list is empty.
- unread = unread list built assuming the range of articles in the group
- is [1,maxlong]. This will be adjusted later when we learn the real
- range of articles in the group.
- status = 'x'. This indicates that we need to learn the range of articles
- for the group.
- onlyRedrawCount = false.
-
- The firstMess, lastMess, and numUnread fields are reset later when
- we learn the real range of articles in the group.
-
- 4. Unsubscribed lines. These lines are left in the newsrc list, moved down
- so that they are contiguous. NewsWatcher doesn't do anything with these
- lines except append them to the end of the newsrc file when it is later
- sent to a remote host. This is for compatibility with UNIX newsreaders.
- ----------------------------------------------------------------------------*/
-
- static OSErr ProcessOneNewsrcLine(void)
- {
- CStr255 groupName;
- char *groupNameStart, *lineStart;
- short index;
- long len;
- OSErr err = noErr;
- Boolean syntaxError;
-
- lineStart = gPos;
- Skip();
- groupNameStart = gPos;
- while (*gPos != ':' && *gPos != '!' && *gPos != CR) gPos++;
- len = gPos - groupNameStart;
- if (len > 255) {
- BlockMoveData(groupNameStart, groupName, 255);
- groupName[255] = 0;
- err = RecordDeletedGroup(groupName, 1);
- if (err != noErr) return err;
- goto exit;
- }
- BlockMoveData(groupNameStart, groupName, len);
- groupName[len] = 0;
- if (len == 0) {
- GetCString(kStrMissingGroupName, groupName);
- err = RecordDeletedGroup(groupName, 1);
- if (err != noErr) return err;
- goto exit;
- }
-
- if (*gPos == ':') {
-
- /* subscribed line - parse it and append the new group to the end
- of gUserGroupArray. */
-
- gPos++;
- index = FindGroupIndex(groupName);
- if (index == -1) {
- err = RecordDeletedGroup(groupName, 2);
- if (err != noErr) return err;
- goto exit;
- }
- gTheGroup.nameOffset = (*gFullGroupArray)[index].nameOffset;
- gTheGroup.firstMess = 1;
- err = GetUnreadList(&syntaxError);
- if (err != noErr) return err;
- if (syntaxError) {
- err = RecordDeletedGroup(groupName, 1);
- if (err != noErr) return err;
- goto exit;
- }
- gTheGroup.status = 'x';
- gTheGroup.onlyRedrawCount = false;
- if (gNumUserGroups >= gNumUserGroupsAllocated) {
- gNumUserGroupsAllocated += 50;
- err = MySetHandleSize(gUserGroupArray, sizeof(TGroup)*gNumUserGroupsAllocated);
- if (err != noErr) return err;
- }
- (*gUserGroupArray)[gNumUserGroups] = gTheGroup;
- gNumUserGroups++;
- return noErr;
-
- } else if (*gPos == '!') {
-
- /* unsubscribed line - copy the line as is to the end of the
- gUnsubscribed memory block. */
-
- while (*gPos != CR) gPos++;
- *gPos++;
- len = gPos - lineStart;
- BlockMoveData(lineStart, gUnsubscribed, len);
- gUnsubscribed += len;
- return noErr;
-
- } else {
-
- err = RecordDeletedGroup(groupName, 1);
- if (err != noErr) return err;
- return noErr;
-
- }
-
- exit:
-
- /* syntax error - skip this line. */
-
- while (*gPos != CR) gPos++;
- gPos++;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetArticleRangeInfo
-
- Query the NNTP server to get current article range info for each group
- in a new user group array.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr GetArticleRangeInfo (void)
- {
- short i = 0;
- short numDeleted = 0;
- char *groupName;
- OSErr err = noErr;
- char state;
-
- err = DisplayStatusMessageNumber(kStrCheckingForNewArticlesStatusMsg);
- if (err != noErr) return err;
-
- err = GetGroupArrayArticleRanges(gUserGroupArray, gNumUserGroups);
- if (err != noErr) return err;
-
- while (i < gNumUserGroups) {
- gTheGroup = (*gUserGroupArray)[i];
- if (gTheGroup.status == 'x') {
- AdjustUnreadList(&gTheGroup);
- gTheGroup.status = ' ';
- (*gUserGroupArray)[i] = gTheGroup;
- i++;
- } else if (gTheGroup.status == 'd') {
- DisposeGroupUnreadList(&gTheGroup);
- gNumUserGroups--;
- if (i < gNumUserGroups) {
- BlockMoveData(*gUserGroupArray+i+1, *gUserGroupArray+i,
- sizeof(TGroup)*(gNumUserGroups-i));
- }
- numDeleted++;
- state = MyHGetState(gGroupNames);
- MyHLock(gGroupNames);
- groupName = *gGroupNames + gTheGroup.nameOffset;
- err = RecordDeletedGroup(groupName, 3);
- MyHSetState(gGroupNames, state);
- if (err != noErr) return err;
- }
- }
-
- if (numDeleted > 0)
- MySetHandleSize(gUserGroupArray, sizeof(TGroup)*gNumUserGroups);
-
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MakeUserGroupArrayFromNewsrc
-
- Create a new user group array from a newsrc-format list.
-
- Entry: newsrc = handle to newsrc-format list.
-
- Exit: function result = error code.
- newsrc = handle to list of unsubscribed groups.
- *groupArray = handle to new user group array.
- *numGroups = number of groups in array.
- *deleted = handle to list of deleted groups.
- ----------------------------------------------------------------------------*/
-
- static OSErr MakeUserGroupArrayFromNewsrc (Handle newsrc, TGroup ***groupArray,
- short *numGroups, Handle *deleted)
- {
- char *gPosEnd;
- OSErr err = noErr;
- char state;
-
- state = MyHGetState(newsrc);
- MyHLockHi(newsrc);
-
- err = MyNewHandle(50*sizeof(TGroup), &gUserGroupArray);
- if (err != noErr) goto exit;
- gNumUserGroups = 0;
- gNumUserGroupsAllocated = 50;
-
- gUnsubscribed = *newsrc;
-
- err = MyNewHandle(0, &gDeleted);
- if (err != noErr) goto exit;
- gDeletedLen = 0;
- gDeletedAllocated = 0;
-
- gPos = *newsrc;
- gPosEnd = gPos + MyGetHandleSize(newsrc);
- while (gPos < gPosEnd) {
- err = ProcessOneNewsrcLine();
- if (err != noErr) goto exit;
- }
-
- MyHSetState(newsrc, state);
- MySetHandleSize(newsrc, gUnsubscribed - *newsrc);
-
- MySetHandleSize(gUserGroupArray, gNumUserGroups*sizeof(TGroup));
-
- err = GetArticleRangeInfo();
- if (err != noErr) goto exit;
-
- *groupArray = gUserGroupArray;
- *numGroups = gNumUserGroups;
- MySetHandleSize(gDeleted, gDeletedLen);
- *deleted = gDeleted;
- return noErr;
-
- exit:
-
- MyHSetState(newsrc, state);
- DisposeGroupArray(gUserGroupArray, gNumUserGroups);
- MyDisposeHandle(gDeleted);
- *groupArray = nil;
- *numGroups = 0;
- *deleted = nil;
- return err;
- }
-
-
- /*----------------------------------------------------------------------------
- MakeUserGroupWindowFromNewsrc
-
- Create a new user group list window from a newsrc-format list.
-
- Entry: newsrc = handle to newsrc-format list.
- title = window title.
- pos = pointer to saved window postion.
-
- Exit: function result = error code.
- *theWindow = pointer to new user group list window
-
- Note: The newsrc block passed as a parameter to this function is either
- disposed or reused by the function. The caller must *NOT* dispose
- or reuse this block.
- ----------------------------------------------------------------------------*/
-
- static OSErr MakeUserGroupWindowFromNewsrc (Handle newsrc, StringPtr title,
- TSavedWindPos *pos, WindowPtr *theWindow)
- {
- TGroup **groupArray;
- Handle deleted = nil;
- WindowPtr wind;
- TWindow **info;
- short numGroups;
- DialogPtr dlg = nil;
- short item;
- OSErr err = noErr;
-
- err = MakeUserGroupArrayFromNewsrc(newsrc, &groupArray, &numGroups, &deleted);
- if (err != noErr) goto exit;
-
- err = MakeUserGroupWindow(title, groupArray, numGroups, pos, &wind);
- if (err != noErr) goto exit;
-
- info = (TWindow**)GetWRefCon(wind);
-
- if (MyGetHandleSize(newsrc) == 0) {
- MyDisposeHandle(newsrc);
- } else {
- (**info).unsubscribed = newsrc;
- }
- newsrc = nil;
-
- if (MyGetHandleSize(deleted) != 0) {
- MyICReadSharedPrefs(kICScreenFont);
- (**info).changed = true;
- SysBeep(0);
- err = MyGetNewDialog(kDeletedGroupsDlg, ok, 0, &dlg);
- if (err != noErr) goto exit;
- RestoreMovableModalDialogPosition(dlg, gPrefs.delGroupsLoc);
- SetItemScrollingTextField(dlg, KDelScrollingTextItem,
- gPrefs.textFont, gPrefs.textSize, true);
- MyHLock(deleted);
- DlgSetScrollingText(dlg, KDelScrollingTextItem, *deleted,
- MyGetHandleSize(deleted));
- DlgSetScrollingTextSelection(dlg, KDelScrollingTextItem, 0, 0);
- MyMovableModalDialog(dlg, DialogFilter, &item);
- SaveMovableModalDialogPosition(dlg, &gPrefs.delGroupsLoc);
- err = DoClose(dlg);
- if (err != noErr) goto exit;
- }
- MyDisposeHandle(deleted);
-
- *theWindow = wind;
- return noErr;
-
- exit:
-
- MyDisposeHandle(deleted);
- MyDisposeHandle(newsrc);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- OpenUserGroupListFile
-
- Open a user group list from a disk file.
-
- Entry: fSpec = the file to be opened.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr OpenUserGroupListFile (FSSpec *fSpec)
- {
- OSErr err = noErr;
- short fRefNum = 0;
- short rRefNum = 0;
- long length;
- Handle newsrc = nil;
- WindowPtr wind;
- TWindowKind kind;
- TWindow **info;
- FSSpec windFile;
- TSavedWindPos pos;
- Handle h;
- Boolean wasChanged;
- AliasHandle alias;
-
- /* Check to see if the file is already open. If it is, bring its
- window to the front. */
-
- for (wind = FrontWindow();
- wind != nil;
- wind = (WindowPtr)((WindowPeek)wind)->nextWindow)
- {
- kind = GetMyWindowKind(wind);
- if (kind == kGroup) {
- info = (TWindow**)GetWRefCon(wind);
- if ((**info).groupKind == kUserGroup && (**info).alias != nil) {
- err = ResolveAlias(nil, (**info).alias, &windFile, &wasChanged);
- if (err == noErr && IsEqualFSSpec(&windFile, fSpec)) {
- MySelectWindow(wind);
- return noErr;
- }
- }
- }
- }
-
- /* Open the file in a new window. */
-
- err = FSpOpenDF(fSpec, fsRdPerm, &fRefNum);
- if (err != noErr) goto exit;
- err = GetEOF(fRefNum, &length);
- if (err != noErr) goto exit;
- err = MyNewHandle(length, &newsrc);
- if (err != noErr) goto exit;
- MyHLock(newsrc);
- err = FSRead(fRefNum, &length, *newsrc);
- MyHUnlock(newsrc);
- if (err != noErr) goto exit;
- MyFSClose(fRefNum, nil);
- fRefNum = 0;
-
- pos.valid = false;
- err = MyFSpOpenResFile(fSpec, fsRdPerm, &rRefNum);
- if (err == noErr) {
- err = MyGet1Resource('WPOS', 128, &h);
- if (err == noErr) {
- if (MyGetHandleSize(h) == sizeof(Point)) {
- pos.valid = true;
- pos.oldFormat = true;
- BlockMoveData(*h, &pos.userState, sizeof(Point));
- } else {
- BlockMoveData(*h, &pos, sizeof(TSavedWindPos));
- }
- }
- MyCloseResFile(rRefNum);
- }
-
- err = MakeUserGroupWindowFromNewsrc(newsrc, fSpec->name, &pos, &wind);
- newsrc = nil;
- if (err != noErr) goto exit;
-
- info = (TWindow**)GetWRefCon(wind);
- err = NewAlias(nil, fSpec, &alias);
- if (err != noErr) goto exit;
- (**info).alias = alias;
-
- return noErr;
-
- exit:
-
- MyDisposeHandle(newsrc);
- if (fRefNum != 0) MyFSClose(fRefNum, nil);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- OpenFTPStream
-
- Open an FTP stream.
-
- Entry: autoFetch = true if startup call to autoFetch group list from host
- or group list window close call to send autoFetched group
- list back to host.
- statusMsgIndex = index in STR# 128 of status message.
-
- Exit: function result = error code.
- *stream = reference to opened stream.
- ----------------------------------------------------------------------------*/
-
- static OSErr OpenFTPStream (Boolean autoFetch, short statusMsgIndex,
- FtpStreamRef *stream)
- {
- OSErr err = noErr;
- NetServerErrInfo serverErrInfo;
-
- while (true) {
- if (!autoFetch || *gPrefs.ftpNewsrcHost == 0 ||
- *gPrefs.ftpNewsrcUsername == 0 ||
- *gPrefs.ftpNewsrcPassword == 0 || *gPrefs.ftpNewsrcPath == 0)
- {
- err = DoHostDialog();
- if (err != noErr) return err;
- }
- err = DisplayStatusMessageNumber(statusMsgIndex);
- if (err != noErr) return err;
- err = FtpOpen(gPrefs.ftpNewsrcHost, gPrefs.ftpNewsrcUsername,
- gPrefs.ftpNewsrcPassword, false, stream);
- if (err == ftpServerErr) {
- FtpGetServerErrInfo(*stream, &serverErrInfo);
- FtpClose(*stream);
- *stream = nil;
- if (serverErrInfo.responseCode != 530) break;
- *gPrefs.ftpNewsrcPassword = 0;
- err = ServerErrorMessage(kStrFTP, serverErrInfo.command, serverErrInfo.response);
- if (err != noErr) return err;
- if (serverErrInfo.responseCode == 530) continue;
- return userCanceledErr;
- } else if (err != noErr) {
- SaveNetErrorInfo(kStrFTP, gPrefs.ftpNewsrcHost);
- return err;
- } else {
- return noErr;
- }
- }
-
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetNewsrcFileFromHost
-
- FTP newsrc file from remote host.
-
- Entry: autoFetch = true if startup call to autoFetch group list from host.
-
- Exit: function result = error code.
- *newsrc = handle to fetched newsrc file.
- ----------------------------------------------------------------------------*/
-
- static OSErr GetNewsrcFileFromHost (Boolean autoFetch, Handle *newsrc)
- {
- FtpStreamRef stream = nil;
- OSErr err = noErr;
- NetServerErrInfo serverErrInfo;
-
- *newsrc = nil;
-
- err = OpenFTPStream(autoFetch, kStrGettingGroupListFromHostStatusMsg, &stream);
- if (err != noErr) return err;
-
- err = FtpGetFile(stream, gPrefs.ftpNewsrcPath, true, newsrc);
- if (err != noErr) goto exit;
-
- err = FtpClose(stream);
- if (err != noErr) goto exit;
-
- return noErr;
-
- exit:
-
- MyDisposeHandle(*newsrc);
- if (err == ftpServerErr) {
- FtpGetServerErrInfo(stream, &serverErrInfo);
- FtpClose(stream);
- err = ServerErrorMessage(kStrFTP, serverErrInfo.command, serverErrInfo.response);
- if (err != noErr) return err;
- return userCanceledErr;
- } else {
- SaveNetErrorInfo(kStrFTP, gPrefs.ftpNewsrcHost);
- return err;
- }
-
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetGroupListFromHost
-
- Get a group list from a host.
-
- Entry: autoFetch = true if startup call to autoFetch group list from host.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr GetGroupListFromHost (Boolean autoFetch)
- {
- Handle newsrc = nil;
- Str255 title;
- OSErr err = noErr;
- TSavedWindPos pos;
- WindowPtr wind;
- TWindow **info;
-
- err = GetNewsrcFileFromHost(autoFetch, &newsrc);
- if (err != noErr) return err;
-
- if (autoFetch) {
- pos = gPrefs.autoFetchWindPos;
- } else {
- pos.valid = false;
- }
-
- GetPString(kStrGetFromHostWindowTitle, title);
- err = MakeUserGroupWindowFromNewsrc(newsrc, title, &pos, &wind);
- newsrc = nil;
- if (err != noErr) goto exit;
-
- info = (TWindow**)GetWRefCon(wind);
- (**info).changed = false;
-
- if (autoFetch) {
- strcpy(gAutoFetchHost, gPrefs.ftpNewsrcHost);
- strcpy(gAutoFetchUsername, gPrefs.ftpNewsrcUsername);
- strcpy(gAutoFetchPassword, gPrefs.ftpNewsrcPassword);
- strcpy(gAutoFetchPath, gPrefs.ftpNewsrcPath);
- (**info).autoFetched = true;
- }
-
- return noErr;
-
- exit:
-
- MyDisposeHandle(newsrc);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoGetGroupListFromHost
-
- Handle the "Get Group List from Host" command.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr DoGetGroupListFromHost (void)
- {
- return GetGroupListFromHost(false);
- }
-
-
-
- /*----------------------------------------------------------------------------
- AutoFetchNewsrcFromHost
-
- Auto-fetch a newsrc from a host at startup.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr AutoFetchNewsrcFromHost (void)
- {
- return GetGroupListFromHost(true);
- }
-
-
-
- /*----------------------------------------------------------------------------
- AppendStrToNewsrc
-
- Append a string to the newsrc.
-
- Entry: str = pointer to string to append to newsrc.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr AppendStrToNewsrc (char *str)
- {
- long len;
- OSErr err = noErr;
-
- len = strlen(str);
- if (gNewsrcLength + len > gNewsrcAllocated) {
- gNewsrcAllocated += 10000;
- err = MySetHandleSizeCritical(gNewsrc, gNewsrcAllocated);
- if (err != noErr) return err;
- }
- BlockMoveData(str, *gNewsrc + gNewsrcLength, len);
- gNewsrcLength += len;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- UpdateAllUnreadLists
-
- Update all the unread lists for a user group list window.
-
- Entry: wind = pointer to user group list window.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr UpdateAllUnreadLists (WindowPtr wind)
- {
- TWindow **info;
- TChild **childListEl;
- OSErr err = noErr;
-
- info = (TWindow**)GetWRefCon(wind);
- for (childListEl = (**info).childList; childListEl != nil;
- childListEl = (**childListEl).next)
- {
- err = UpdateUnreadList((**childListEl).childWindow);
- if (err != noErr) return err;
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MakeNewsrcFromUserGroupListWindow
-
- Make a newsrc list from a user group list window.
-
- Entry: wind = pointer to user group list window.
-
- Exit: function result = error code.
- *newsrc = handle to newsrc list.
- *length = length of newsrc list.
- ----------------------------------------------------------------------------*/
-
- static OSErr MakeNewsrcFromUserGroupListWindow (WindowPtr wind,
- Handle *newsrc, long *length)
- {
- TWindow **info;
- ListHandle theList;
- TGroup **groupArray;
- Cell theCell;
- short numCells, index, cellDataLen;
- TGroup theGroup;
- long first, last;
- TUnread **unread;
- CStr255 tmpStr;
- Boolean firstRange;
- OSErr err = noErr;
- char state;
-
- state = MyHGetState(gGroupNames);
-
- err = MyNewHandleCritical(10000, &gNewsrc);
- if (err != noErr) return err;
- gNewsrcLength = 0;
- gNewsrcAllocated = 10000;
-
- info = (TWindow**)GetWRefCon(wind);
- theList = (**info).theList;
- groupArray = (**info).groupArray;
-
- err = UpdateAllUnreadLists(wind);
- if (err != noErr) goto exit;
-
- theCell.h = 0;
- numCells = (**theList).dataBounds.bottom;
- MyHLock(gGroupNames);
- for (theCell.v = 0; theCell.v < numCells; theCell.v++) {
- cellDataLen = 2;
- LGetCell(&index, &cellDataLen, theCell, theList);
- theGroup = (*groupArray)[index];
- err = AppendStrToNewsrc(*gGroupNames + theGroup.nameOffset);
- if (err != noErr) goto exit;
- err = AppendStrToNewsrc(": ");
- if (err != noErr) goto exit;
- first = 1;
- firstRange = true;
- for (unread = theGroup.unread; unread != nil; unread = (**unread).next) {
- last = (**unread).firstUnread-1;
- if (first <= last) {
- if (first < last) {
- sprintf(tmpStr, "%lu-%lu", first, last);
- } else {
- sprintf(tmpStr, "%lu", first);
- }
- if (!firstRange) {
- err = AppendStrToNewsrc(",");
- if (err != noErr) goto exit;
- }
- err = AppendStrToNewsrc(tmpStr);
- if (err != noErr) goto exit;
- firstRange = false;
- }
- first = (**unread).lastUnread + 1;
- }
- last = theGroup.lastMess;
- if (first <= last) {
- if (first < last) {
- sprintf(tmpStr, "%lu-%lu", first, last);
- } else {
- sprintf(tmpStr, "%lu", first);
- }
- if (!firstRange) {
- err = AppendStrToNewsrc(",");
- if (err != noErr) goto exit;
- }
- err = AppendStrToNewsrc(tmpStr);
- if (err != noErr) goto exit;
- }
- err = AppendStrToNewsrc(CRSTR);
- if (err != noErr) goto exit;
- }
- MyHSetState(gGroupNames, state);
-
- MySetHandleSize(gNewsrc, gNewsrcLength);
-
- *newsrc = gNewsrc;
- *length = gNewsrcLength;
-
- return noErr;
-
- exit:
-
- MyHSetState(gGroupNames, state);
- MyDisposeHandle(gNewsrc);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- SaveFile
-
- Save a user group list window to a disk file.
-
- Entry: wind = pointer to user group list window.
- fSpec = the file.
- scriptTag = script code.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr SaveFile (WindowPtr wind, FSSpec *fSpec, ScriptCode scriptTag)
- {
- Handle newsrc = nil;
- long length;
- short fRefNum = 0;
- short rRefNum = 0;
- OSErr err = noErr;
- Boolean empty;
- Boolean savedCriticalSeq;
-
- BeginCriticalMemorySequence(&savedCriticalSeq);
-
- err = MakeNewsrcFromUserGroupListWindow(wind, &newsrc, &length);
- if (err != noErr) goto exit;
-
- err = OpenDataForkWriteCreateIfMissing(fSpec, kNewsWatcherSignature,
- kSavedUserGroupListFileType, scriptTag, false, &fRefNum, &empty);
- if (err != noErr) goto exit;
-
- MyHLock(newsrc);
- err = MyFSWriteNoCache(fRefNum, &length, *newsrc, nil);
- MyDisposeHandle(newsrc);
- newsrc = nil;
- MyFSClose(fRefNum, nil);
- fRefNum = 0;
-
- err = OpenResFileWriteCreateIfMissing(fSpec, kNewsWatcherSignature,
- kSavedUserGroupListFileType, scriptTag, &rRefNum);
- if (err != noErr) goto exit;
-
- err = WriteProgramNameResource(kStrNewsWatcher);
- if (err != noErr) goto exit;
-
- err = SaveWindPosAsResource(wind);
- if (err != noErr) goto exit;
-
- MyCloseResFile(rRefNum);
-
- EndCriticalMemorySequence(savedCriticalSeq);
-
- return noErr;
-
- exit:
-
- MyDisposeHandle(newsrc);
- if (fRefNum != 0) MyFSClose(fRefNum, nil);
- if (rRefNum != 0) MyCloseResFile(rRefNum);
- EndCriticalMemorySequence(savedCriticalSeq);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoSaveAs
-
- Handle the "Save As" command.
-
- Entry: wind = pointer to user group list window.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr DoSaveAs (WindowPtr wind)
- {
- StandardFileReply reply;
- TWindow **info;
- Str255 fName;
- Str255 prompt;
- OSErr err = noErr;
- AliasHandle alias;
-
- info = (TWindow**)GetWRefCon(wind);
-
- GetWTitle(wind, fName);
- GetPString(kStrSaveGroupListAs, prompt);
- MyStandardPutFile(prompt, fName, &reply,
- gPrefs.savedUGLDefaultFolder ? gPrefs.savedUGLDefaultFolderAlias : nil);
- if (!reply.sfGood) return userCanceledErr;
-
- err = SaveFile(wind, &reply.sfFile, reply.sfScript);
- if (err != noErr) return err;
- SetWTitle(wind, reply.sfFile.name);
- err = NewAlias(nil, &reply.sfFile, &alias);
- if (err != noErr) return err;
- MyDisposeHandle((**info).alias);
- (**info).alias = alias;
- (**info).changed = false;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoSave
-
- Handle the "Save" command.
-
- Entry: wind = pointer to user group list window.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr DoSave (WindowPtr wind)
- {
- TWindow **info;
- FSSpec fSpec;
- OSErr err = noErr;
- AliasHandle alias;
- Boolean wasChanged;
-
- info = (TWindow**)GetWRefCon(wind);
- alias = (**info).alias;
- if (alias != nil) {
- err = ResolveAlias(nil, alias, &fSpec, &wasChanged);
- if (err == noErr) {
- err = SaveFile(wind, &fSpec, smSystemScript);
- if (err != noErr) return err;
- (**info).changed = false;
- return noErr;
- } else {
- return DoSaveAs(wind);
- }
- } else {
- return DoSaveAs(wind);
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- CheckForSave
-
- Ask the user if he wishes to save a user group list window, and save it
- if the user says yes.
-
- Entry: wind = pointer to user group list window.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr CheckForSave (WindowPtr wind)
- {
- TWindow **info;
- DialogPtr dlg = nil;
- short item;
- Str255 fName;
- OSErr err = noErr;
-
- info = (TWindow**)GetWRefCon(wind);
- if ((**info).okToCloseIfChanged) return noErr;
- if (gDone && gPrefs.autoSaveOnQuit && (**info).alias != nil) return DoSave(wind);
- GetWTitle(wind, fName);
- ParamText(fName, "\p", "\p", "\p");
- err = MyGetNewDialog(kCheckSaveID, ok, cancel, &dlg);
- if (err != noErr) return err;
- SetItemKeyEquivalent(dlg, 3, 'D');
- SysBeep(0);
- MyModalDialog(dlg, gDialogFilterUPP, &item);
- err = DoClose(dlg);
- if (err != noErr) return err;
- switch (item) {
- case 1: /* save */
- return DoSave(wind);
- case 2: /* cancel */
- return userCanceledErr;
- case 3: /* don't save */
- return noErr;
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- SendNewsrcFileToHost
-
- FTP newsrc file to remote host.
-
- Entry: autoFetch = true if sending back autofetched newsrc file.
- newsrc = handle to newsrc file.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr SendNewsrcFileToHost (Boolean autoFetch, Handle newsrc)
- {
- FtpStreamRef stream;
- OSErr err = noErr;
- NetServerErrInfo serverErrInfo;
-
- err = OpenFTPStream(autoFetch, kStrSendingGroupListToHostStatusMsg, &stream);
- if (err != noErr) return err;
-
- err = FtpPutFile(stream, gPrefs.ftpNewsrcPath, true, newsrc);
- if (err != noErr) goto exit;
-
- err = FtpClose(stream);
- if (err != noErr) goto exit;
-
- return noErr;
-
- exit:
-
- if (err == ftpServerErr) {
- FtpGetServerErrInfo(stream, &serverErrInfo);
- FtpClose(stream);
- err = ServerErrorMessage(kStrFTP, serverErrInfo.command, serverErrInfo.response);
- if (err != noErr) return err;
- return userCanceledErr;
- } else {
- SaveNetErrorInfo(kStrFTP, gPrefs.ftpNewsrcHost);
- return err;
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoSendGroupListToHost
-
- Handle the "Send Group List to Host" command.
-
- Entry: wind = pointer to window.
- autoFetch = true if sending back autofetched group list.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr DoSendGroupListToHost (WindowPtr wind, Boolean autoFetch)
- {
- TWindow **info;
- Handle newsrc = nil;
- Handle tempHandle = nil;
- Handle unsubscribed;
- long newsrcLen, unsubscribedLen;
- OSErr err = noErr;
- OSErr err1;
- Boolean savedCriticalSeq;
-
- BeginCriticalMemorySequence(&savedCriticalSeq);
-
- info = (TWindow**)GetWRefCon(wind);
-
- if (autoFetch) {
- strcpy(gPrefs.ftpNewsrcHost, gAutoFetchHost);
- strcpy(gPrefs.ftpNewsrcUsername, gAutoFetchUsername);
- strcpy(gPrefs.ftpNewsrcPassword, gAutoFetchPassword);
- strcpy(gPrefs.ftpNewsrcPath, gAutoFetchPath);
- }
-
- err = MakeNewsrcFromUserGroupListWindow(wind, &newsrc, &newsrcLen);
- if (err != noErr) goto exit;
-
- unsubscribed = (**info).unsubscribed;
-
- if (unsubscribed != nil) {
- unsubscribedLen = MyGetHandleSize(unsubscribed);
- if (!MemoryAvailable(unsubscribedLen)) {
- if (!HaveModernTempMemory()) {
- err = memFullErr;
- goto exit;
- }
- err = MyTempNewHandle(unsubscribedLen, &tempHandle);
- if (err != noErr) goto exit;
- BlockMoveData(*unsubscribed, *tempHandle, unsubscribedLen);
- MyDisposeHandle(unsubscribed);
- unsubscribed = tempHandle;
- (**info).unsubscribed = nil;
- }
- err = MyHandAndHand(unsubscribed, newsrc);
- if (err != noErr) goto exit;
- }
-
- err = SendNewsrcFileToHost(autoFetch, newsrc);
- if (err != noErr) goto exit;
-
- (**info).changed = false;
-
- exit:
-
- MyDisposeHandle(newsrc);
- if (tempHandle != nil) {
- err1 = MyNewHandleCritical(unsubscribedLen, &unsubscribed);
- if (err1 == noErr) {
- BlockMoveData(*tempHandle, *unsubscribed, unsubscribedLen);
- (**info).unsubscribed = unsubscribed;
- }
- MyDisposeHandle(tempHandle);
- }
- EndCriticalMemorySequence(savedCriticalSeq);
- return err;
- }
-